package com.jd.jobkeeper.server.scheduler;

import com.jd.jobkeeper.server.service.IdGenerator;
import com.jd.jobkeeper.server.service.ServerInfoService;
import com.jd.jobkeeper.server.service.SnowFlake;
import com.jd.jobkeeper.server.transport.TcpServer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.Lifecycle;
import org.springframework.context.SmartLifecycle;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.event.ContextStoppedEvent;
import org.springframework.stereotype.Component;
import reactor.core.Disposable;
import reactor.core.scheduler.Scheduler;
import reactor.core.scheduler.Schedulers;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.util.concurrent.TimeUnit;

@Component
public class SchedulerContext implements SmartLifecycle {

    private static final Logger log = LoggerFactory.getLogger(SchedulerContext.class);

    boolean isRunning = false;
    final Scheduler scheduler;
    final TcpServer schedulerServer;
    final ServerInfoService serverInfoService;
    final long heartBeatInterval = 5;
    static final int MAX_HEART_BEAT_FAILURE_TIMES = 3;
    volatile int heartBeatFailTimes = MAX_HEART_BEAT_FAILURE_TIMES;
    Disposable heartTask;
    IdGenerator idGenerator;

    public SchedulerContext(TcpServer schedulerServer,
                            ServerInfoService serverInfoService) {
        this.schedulerServer = schedulerServer;
        this.serverInfoService = serverInfoService;
        this.scheduler = Schedulers.newBoundedElastic(200, 1024, "db-thread-scheduler");
    }

    public boolean isHealth() {
        return heartBeatFailTimes < MAX_HEART_BEAT_FAILURE_TIMES;
    }

    public Scheduler getScheduler() {
        return scheduler;
    }

    public IdGenerator idGenerator() {
        return idGenerator;
    }


    @Override
    public void start() {
        this.isRunning = true;
        this.schedulerServer.start();
        this.serverInfoService.register();
        this.heartTask = scheduler.schedulePeriodically(this::heartBeatInTick, 0, heartBeatInterval, TimeUnit.SECONDS);
        this.idGenerator = new SnowFlake(0, serverInfoService.currentServer().getId());
    }

    public void stop(){
        this.isRunning = false;
        log.info("Scheduler is stopping....");
        if (this.heartTask != null) {
            this.heartTask.dispose();
        }
        this.serverInfoService.unregister();

        this.heartBeatFailTimes = MAX_HEART_BEAT_FAILURE_TIMES;
        //Stop schedulerServer
    }

    @Override
    public boolean isRunning() {
        return isRunning;
    }

    public void heartBeatInTick() {
        try {
            serverInfoService.heartBeat();
            heartBeatFailTimes = 0;
        } catch (Exception e) {
            log.warn("Update heart beat fail in heartBeatInTick method");
            heartBeatFailTimes++;
        }

    }


}
